package server;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import pojo.*;
import util.CustomClassLoader;

import javax.lang.model.util.Elements;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.*;
import java.util.logging.Logger;

/**
 * Minicat的主类
 */
public class Bootstrap {

    /**
     * 定义socket监听的端口号
     */
    private List<Integer> ports = new ArrayList<>();

    /**
     * 定义线程池
     */
    private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            10,
            50,
            100L,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(50),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy()
    );

    private File tempFile;

    /**
     * Minicat 的程序启动入口
     *
     * @param args
     */
    public static void main(String[] args) {

        Bootstrap bootstrap = new Bootstrap();
        try {
            // 启动Minicat
            bootstrap.start();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Minicat启动需要初始化展开的一些操作
     */
    public void start() throws Exception {

        // 加载解析相关配置server.xml
        loadServer();

        // 加载解析相关的配置，web.xml
        loadServlet();

        for (Integer port : ports) {
            ServerSocket serverSocket = new ServerSocket(port);
            RequestProcessor requestProcessor = new RequestProcessor(serverSocket, servletMap);
            System.out.println("=====>>>Minicat start on port：" + port);

            System.out.println("=========>>>>>>使用线程池进行多线程改造");
            /*
                多线程改造（使用线程池）
             */
            //requestProcessor.start();
            threadPoolExecutor.execute(requestProcessor);
        }
    }


    private Map<String, HttpServlet> servletMap = new HashMap<String, HttpServlet>();

    private Server server = new Server();

    private void loadServer() {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("server.xml");
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();

            List<Service> services = new ArrayList<>();

            Element serverElement = (Element) rootElement.selectSingleNode("/Server");

            // 一个Server可以包含多个Service
            List<Element> serviceElements = serverElement.selectNodes("Service");

            for (Element serviceElement : serviceElements) {
                // 解析Connector
                List<Connector> connectors = new ArrayList<>();
                List<Element> connectorElements = serviceElement.selectNodes("Connector");
                for (Element connectorElement : connectorElements) {
                    String port = connectorElement.attributeValue("port");
                    Connector connector = new Connector(Integer.valueOf(port));
                    connectors.add(connector);
                }

                // 解析Engine
                Element engineElement = (Element) serviceElement.selectSingleNode("Engine");
                // 解析Host
                List<Host> hosts = new ArrayList<>();
                List<Element> hostElements = engineElement.selectNodes("Host");
                for (Element hostElement : hostElements) {
                    String name = hostElement.attributeValue("name");
                    String appBase = hostElement.attributeValue("appBase");
                    List<Element> contextElements = hostElement.selectNodes("Context");
                    List<Context> contexts = new ArrayList<>();
                    for (Element contextElement : contextElements) {
                        String contextName = contextElement.attributeValue("name");
                        String contextPath = contextElement.attributeValue("path");
                        Context context = new Context(contextName, contextPath);
                        contexts.add(context);
                    }
                    Host host = new Host(name, appBase, contexts);
                    hosts.add(host);
                }
                Engine engine = new Engine(hosts);

                Service service = new Service(connectors, engine);
                services.add(service);
            }
            server.setServices(services);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 加载解析web.xml，初始化Servlet
     */
    private void loadServlet() {

        // 获取appBase路径，根据路径扫描获得web.xml文件
        // 因为都是已知内容，就不加判空了
        for (Service service : server.getServices()) {

            // = =协议什么的太麻烦，干脆就全假装是Http协议了
            for (Connector connector : service.getConnectors()) {
                if (connector.getPort() != null) {
                    ports.add(connector.getPort());
                }
            }

            for (Host host : service.getEngine().getHosts()) {
                String appBase = host.getAppBase();
                for (Context context : host.getContexts()) {

                    String name = context.getName();
                    String path = context.getPath();
                    String scanPath = appBase;
                    // 如果是相对路径
                    if (!isAbsolutePath(appBase)) {
                        scanPath = System.getProperty("user.dir") + File.separator + appBase;
                    }
                    scanPath += File.separator + path;
                    scanPath = scanPath.replaceAll("\\\\","/").replaceAll("/+", "/");

                    // 初始化临时文件
                    tempFile = null;
                    // 遍历文件获取web.xml
                    scanPackage(scanPath);
                    if (tempFile == null) {
                        System.out.println("MiniTomcat 只支持web工程，该工程" + scanPath + "为非web工程");
                        continue;
                    }

                    SAXReader saxReader = new SAXReader();

                    try {

                        InputStream inputStream = new FileInputStream(tempFile);

                        Document document = saxReader.read(inputStream);
                        Element rootElement = document.getRootElement();

                        List<Element> selectNodes = rootElement.selectNodes("//servlet");
                        for (int i = 0; i < selectNodes.size(); i++) {
                            Element element = selectNodes.get(i);
                            // <servlet-name>lagou</servlet-name>
                            Element servletnameElement = (Element) element.selectSingleNode("servlet-name");
                            String servletName = servletnameElement.getStringValue();
                            // <servlet-class>server.LagouServlet</servlet-class>
                            Element servletclassElement = (Element) element.selectSingleNode("servlet-class");
                            String servletClass = servletclassElement.getStringValue();

                            // 根据servlet-name的值找到url-pattern
                            Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
                            // /lagou
                            String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();

                            // 编译项目路径
                            String classDir = tempFile.getParent();
                            // 当前项目路径
                            String projectPath = System.getProperty("user.dir");

                            if (classDir.equals(projectPath)) {
                                servletMap.put(path + urlPattern, (HttpServlet) Class.forName(servletClass).newInstance());
                            } else {
                                projectPath += servletClass;
                                projectPath.replaceAll(".", "/");
                                projectPath += ".class";
                                CustomClassLoader cus = new CustomClassLoader(classDir);

                                Class<?> servlet = cus.findClass(servletClass);
                                servletMap.put(path + urlPattern, (HttpServlet) servlet.newInstance());
                            }
                        }


                    } catch (DocumentException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    }


                }
            }
        }

    }

    private void scanPackage(String scanPath) {
        File pack = new File(scanPath);

        File[] files = pack.listFiles();

        for (File file : files) {
            if (file.isDirectory()) { // 子package
                // 递归
                scanPackage(scanPath + "/" + file.getName());  // com.lagou.demo.controller
            } else if (file.getName().equals("web.xml")) {
                tempFile = file;
            }
        }
    }

    public Boolean isAbsolutePath(String path){
        if (null == path || "".equals(path)) {
            return null;
        }
        // os 或者 windows
        return path.startsWith("/") || path.contains(":");
    }
}
